package cn.personal.study.springbootdemo.service;

import java.util.List;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jpa.HibernateEntityManager;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;

import cn.personal.study.springbootdemo.domain.Student;
import cn.personal.study.springbootdemo.domain.dto.mapper.StudentMapper;
import cn.personal.study.springbootdemo.domain.dto.request.CreateStudentRequestDTO;
import cn.personal.study.springbootdemo.domain.dto.request.QueryStudentRequestDTO;
import cn.personal.study.springbootdemo.domain.dto.request.UpdateStudentRequestDTO;
import cn.personal.study.springbootdemo.domain.dto.response.CreateStudentResponseDTO;
import cn.personal.study.springbootdemo.domain.dto.response.QueryStudentResponseDTO;
import cn.personal.study.springbootdemo.domain.dto.response.UpdateStudentResponseDTO;
import cn.personal.study.springbootdemo.domain.dto.response.ViewStudentResponseDTO;
import cn.personal.study.springbootdemo.repository.StudentRepository;
import io.micrometer.core.instrument.util.StringUtils;

/**
 * 学生服务
 *
 * @author duliurui
 * @since 1.0.0
 */
@Transactional(rollbackFor = Exception.class)
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    /**
     * 默认的注入方式，当且仅当只有一个EntityManager的时候才适用，如果配置了多个EntityManager
     * 那么请需要配置别名@PersistenceContext("name")
     */
    @PersistenceContext
    private EntityManager entityManager;


    public ViewStudentResponseDTO viewStudent(String studentId) {
        Student student = studentRepository.findById(studentId).orElse(null);
        ViewStudentResponseDTO responseDTO = new ViewStudentResponseDTO();
        if (student == null) {
            throw new RuntimeException("学生不存在");
        }
        responseDTO.setId(student.getId());
        responseDTO.setName(student.getName());
        responseDTO.setAge(student.getAge());
        return responseDTO;
    }


    /**
     * 动态分页查询Student信息。
     * 查询条件需要动态构建。
     * 基于这2个原因，采用Criteria方式构建查询所需的Query
     *
     * @param queryUserRequestDTO 用户查询条件
     * @param pageable            分页对象
     * @return 用户分页列表数据
     */
    public Page<QueryStudentResponseDTO> queryStudent(QueryStudentRequestDTO queryUserRequestDTO, Pageable pageable) {
        Criteria criteria = ((HibernateEntityManager) entityManager).getSession().createCriteria(Student.class, "t")
                //结果输出为List集合
                .setProjection(Projections.projectionList()
                        //添加属性，add(propertyName,alias)
                        .add(Projections.property("t.id"), "id")
                        .add(Projections.property("t.name"), "name")
                        .add(Projections.property("t.age"), "age")
                )
                //转化为queryStudentResponseDTO，其中add(propertyName,alias)中的alias为queryStudentResponseDTO对应的属性名称
                .setResultTransformer(Transformers.aliasToBean(QueryStudentResponseDTO.class));

        if (queryUserRequestDTO.getAge() > 0) {
            criteria.add(Restrictions.eq("t.age", queryUserRequestDTO.getAge()));
        }

        if (queryUserRequestDTO.getName() != null) {
            // 等价于 where name like '%?%'
            criteria.add(Restrictions.like("t.name", queryUserRequestDTO.getName(), MatchMode.ANYWHERE));
        }
        int pageNum = pageable.getPageNumber();
        int pageSize = pageable.getPageSize();

        @SuppressWarnings("unchecked")
        List<QueryStudentResponseDTO> list = (List<QueryStudentResponseDTO>) criteria
                .setFirstResult(pageNum * pageSize)
                .setMaxResults(pageSize).list();

        //获取总记录数
        Integer totalResult = ((Number) criteria
                .setFirstResult(0)
                .setProjection(Projections.rowCount())
                .uniqueResult())
                .intValue();
        return new PageImpl<QueryStudentResponseDTO>(list, pageable, totalResult);
    }

    public CreateStudentResponseDTO createStudent(CreateStudentRequestDTO requestDTO) {
        Student student = studentRepository.save(StudentMapper.createStudentRequestDTOToStudent(requestDTO));
        return StudentMapper.studentToCreateStudentResponseDTO(student);
    }

    public UpdateStudentResponseDTO updateStudent(UpdateStudentRequestDTO requestDTO) {
        Optional<Student> existedStudentOptional = studentRepository.findById(requestDTO.getId());
        if (existedStudentOptional.isPresent()) {
            Student existedStudent = existedStudentOptional.get();
            if (StringUtils.isNotBlank(requestDTO.getName())) {
                existedStudent.setName(requestDTO.getName());
            }
            if (StringUtils.isNotBlank(requestDTO.getLoves())) {
                existedStudent.setLoves(requestDTO.getLoves());
            }
            if (requestDTO.getAge() > 0) {
                existedStudent.setAge(requestDTO.getAge());
            }
            Student savedStudent = studentRepository.save(existedStudent);
            return StudentMapper.studentToUpdateStudentResponseDTO(savedStudent);

        }
        throw new RuntimeException(String.format("需要更新的学生: %s 不存在", requestDTO.getId()));
    }

    public void delete(String studentId) {
        Optional<Student> existedStudentOptional = studentRepository.findById(studentId);
        if (existedStudentOptional.isPresent()) {
            studentRepository.deleteById(studentId);
            return;
        }
        throw new RuntimeException(String.format("需要删除的学生: %s 不存在", studentId));
    }
}
